package com.data;

import com.vo.*;
import java.net.URL;
import java.util.*;

/**
 * Classe responsavel pelo gerenciamento geral da informacao do sistema,
 * tal como as listas de objetos.
 * @author Jean Vasconcelos, Carla Sukeyosi, Matheus Emanuel, Heitor Carvalho
 */
public class DataManager 
{
    private IODataHandler dataHandler;
    private List<Contact> cList;
    private List<Event> eList;
    private User user;
    private List<Group> gList;
    private List<Event> future;
    private List<Event> past;
    
    /**
     * Prepara a classe para ser usada ao ser instanciada.
     * Instancia objetos e carrega listas e informacoes do usuario.
     */
    public DataManager ()
    {
        dataHandler = new IODataHandler();
        cList = new ArrayList<Contact>();
        eList = new ArrayList<Event>();
        gList = new ArrayList<Group>();
        future = new ArrayList<Event>();
        past = new ArrayList<Event>();
        
        //Carregando informacoes do usuario do sistema, cast de Object
        //para User.
        Object aux =  dataHandler.loadUserInfo(IODataHandler.userPath);
        if (aux != null)
            user = (User) aux;
        //Carregando os contatos do disco utilizando Iterator
        List<Object> helper = dataHandler.loadData(IODataHandler.contactPath);
        Iterator it = helper.iterator();

        while (it.hasNext())
        {
            //Cast de Object para Contact
            Contact c = (Contact) it.next();
            cList.add(c);
        }

        //Carregando os eventos do disco utilizando Iterator
        helper = dataHandler.loadData(IODataHandler.eventPath);
        it = helper.iterator();

        while (it.hasNext())
        {
            //Cast de Object para Event
            Event e = (Event) it.next();
            eList.add(e);
        }
        divideEventList();
        
        //Carregando os grupos do disco utilizando Iterator
        List<String> names = dataHandler.loadGroupNames(IODataHandler.groupPath);
        it = names.iterator();

        while (it.hasNext())
        {
            //Cast de Object para Group
            String name = (String) it.next();
            Group g = new Group(name);
            gList.add(g);
        }
        
        loadGroupList();
    }
    
    /**
     * Recupera as informacoes do usuario
     * @return objeto User com as informacoes do usuario
     */
    public User getUser() 
    {
        return user;
    }

    /**
     * Altera as informacoes do usuario
     * @param user novas informacoes do usuario
     */
    public void setUser(User user) 
    {
        this.user = user;
    }
    
    /**
     * Procura um Contato a partir do nome ou parte dele.
     * @param name Nome do Contato.
     * @return List com possiveis Contatos. 
     */
    public List<Contact> contactSearch (String name) throws Exception
    {
        List<Contact> result = new ArrayList<Contact>();
        Iterator it = cList.iterator();
        while (it.hasNext())
        {
            Contact c = (Contact) it.next();
            if (c.getName().toLowerCase().contains(name.toLowerCase()))
            {
                result.add(c);
            }
        }
        return result;
    }
    
    /**
     * Adiciona um Contato na lista de Contatos.
     * @param c Contato a ser adicionado.
     * @param old contato antigo que foi atualizado.
     * @return true, no caso do contato ser adicionado a lista com sucesso.
     */
    public boolean addContact (Contact c, Contact old) throws Exception
    {
        //Codigo para atualizacao do grupo ao qual o contato ira pertencer
        if (old == null)
        {
            for (Group g : gList)
            {
                if (c.getGroups().contains(g.hashCode()))
                    g.addContact(c);
            }
        } 
        else 
        {
            for (Group g : gList)
            {
                g.removeContact(old);
                
                if (c.getGroups().contains(g.hashCode()))
                    g.addContact(c);
            }
        }
        //Fim codigo de grupo
        
        if (c == null)
            return false;
        if (!(old != null && cList.remove(old)))
            throw new Exception("Erro ao adicionar contato atualizado a lista"
                    + "de contatos.");
        return cList.add(c);
    }
    
    /**
     * Oush pronto comenta tu! //TO DO
     */
    private void divideEventList ()
    {
        //Dividindo os eventos em passado e futuro
        for (Event e : eList)
        {
            if (e.getInterval().getStartCalendar().before(Calendar.getInstance()))
                if (!past.contains(e))
                    past.add(e);
            if (e.getInterval().getStartCalendar().after(Calendar.getInstance()))
                if (!future.contains(e))
                    future.add(e);
        }
    }
    
    /**
     * Adiciona um Evento na lista de Eventos.
     * @param e Evento a ser adicionado.
     * @param old Evento antigo que foi atualizado.
     * @return true, no caso do evento ser adicionado a lista com sucesso.
     */
    public boolean addEvent (Event e, Event old) throws Exception
    {
        if (e == null)
            return false;
        if (old != null)
        {
            eList.remove(old);
            removeItem(old);
            if (past.contains(old))
                past.remove(old);
            else
                future.remove(old);
        }
        
        boolean result = eList.add(e);
        if (result)
            divideEventList();
        return result;
    }
    
    /**
     * Adiciona um grupo na lista de Grupos.
     * @param g Grupo a ser adicionado.
     * @param old Grupo antigo que foi atualizado.
     * @return true, no caso do grupo ser adicionado a lista com sucesso.
     */
    public boolean addGroup (Group g, Group old) throws Exception
    {
        if (g == null)
            return false;
        
        if (old != null)
        {
            if (!gList.remove(old))
                throw new Exception("Erro ao adicionar grupo atualizado a lista"
                        + "de grupos.");
            return false;
        }
        
        return gList.add(g);
    }
    
    /**
     * Recupera a lista de eventos futuros.
     * @return List contendo todos os Eventos futuros.
     */
    public List<Event> getFutureEventList ()
    {
        
        return future;
    }
    
    /**
     * Recupera a lista de eventos passados.
     * @return List contendo todos os Eventos passados.
     */
    public List<Event> getPastEventList ()
    {
        return past;
    }

    /**
     * Recupera a lista de todos os eventos.
     * @return lista com todos os eventos.
     */
    public List<Event> geteList() 
    {
        return eList;
    }
    
    /**
     * Recupera a lista de contatos.
     * @return List contendo todos os contatos.
     */
    public List<Contact> getContactList ()
    {
        return cList;
    }
    
    /**
     * Recupera a lista de grupos de contatos
     * @return lista de grupos de contatos
     */
    public List<Group> getGroupList() 
    {
        return gList;
    }
    
    /**
     * Remove um item das listas dependendo de seu tipo.
     * @param obj a ser excluido.
     * @return true, no caso do objeto ser removido com sucesso.
     */
    public boolean removeItem (Object obj) throws Exception
    {
        //Codigo para remocao de contato do sistema
        if (obj.getClass().equals(new Contact("", "", Calendar.getInstance()).getClass()))
        {
            //Removendo o contato dos grupos
            Contact c = (Contact) obj;
            
            for (Group g : gList)
            {
                List<Contact> list = g.getContactList();
                if (list.contains(c))
                {
                    list.remove(c);
                }
            }//Fim do codigo de remocao do contato dos grupos
            
            //Codigo para remocao do contato nos eventos
            for (Event e : eList)
            {
                if (e.getContacts().contains(c))
                    e.removeContact(c);
            }//fim codigo para remocao do contato nos eventos
            
            boolean result = cList.remove(c);
            dataHandler.remove(c, IODataHandler.contactPath);
            
            if (c.getImagePath() != null)
            {
                dataHandler.removeImage(c.getImagePath());
            }
            
            return result;
        }//Fim codigo para remocao de contato
        
        //Codigo para remocao de evento do sistema
        else if (obj.getClass().equals(new Event("", "", new Interval()).getClass()))
        {
            Event e = (Event) obj;
            //Codigo para remocao do evento nos contatos
            for (Contact c: cList)
            {
                if (c.getEvents().contains(e))
                    c.removeEvents(e);
            }//fim do codigo de remocao de evento dos contatos
            
            boolean result = false;
            
            if (eList.contains(e))
                result = eList.remove(e);
            if (past.contains(e))
                past.remove(e);
            if (future.contains(e))
                future.remove(e);

            dataHandler.remove(e, IODataHandler.eventPath);
            
            List<Note> notes = e.getNotes();
            
            for (Note n : notes)
            {
                List<URL> urls = n.getURLs();
                for (URL url : urls)
                    dataHandler.removeImage(url.toURI().toURL().toString());
            }
            
            return result;
        }//Fim do codigo de remocao de evento
        
        //Codigo para remocao de um grupo do sistema
        if (obj.getClass().equals(new Group("")))
        {
            Group g = (Group) obj;
            
            for (Contact c : cList)
            {
                List<Integer> group = c.getGroups();
                for (Integer i : group)
                {
                    if (g.hashCode() == i)
                        group.remove(i);
                }
                //VERIFICAR FUNCIONAMENTO [DUVIDA NA REFERENCIA DA LISTA
                //Verificar se realmente o grupo esta sendo removido do contato
            }
            return gList.remove(g);
        }//Fim codigo da remocao de grupo
        else
        {
            throw new Exception("Tentativa de remover objeto invalido.");
        }
    }
    /**
     * Salva todos os objetos das listas e o objeto user, utilizando-se da classe
     * IODataHandler e seu metodo saveData.
     */
    public boolean saveAll ()
    {
        //Salvando informacoes do usuario no disco
        if (!dataHandler.saveData(user, IODataHandler.userPath, true))
        {
                System.out.println("Erro ao salvar user.");
                return false;
        }
        
        //Salvando contatos no disco
        for (Contact c : cList)
        {
            if (!dataHandler.saveData(c, IODataHandler.contactPath, false))
            {
                System.out.println("Erro ao salvar contatos.");
                return false;
            }
        }
        
        //Salvando eventos no disco
        for (Event e : eList)
        {
            if (!dataHandler.saveData(e, IODataHandler.eventPath, false))
            {
                System.out.println("Erro ao salvar eventos.");
                return false;
            }
        }
        
        //Salvando nomes dos grupos no disco
        ArrayList<String> names = new ArrayList<String>();
        
        for (Group g : gList)
        {
            names.add(g.getName());
        }
        
        if (!dataHandler.saveGroupNames(names, IODataHandler.groupPath))
        {
            System.out.println("Erro ao salvar grupos.");
            return false;
        }
        
        return true;
    }
    
    /**
     * Carrega as informacoes dos grupos de contatos
     */
    private void loadGroupList() 
    {
        for (Group g : gList)
        {
            for (Contact c : cList)
            {
                if (c.getGroups().contains(g.hashCode()))
                    g.addContact(c);
            }
        }
    }
    
}